package com.guaigen.train.generator.gen;

import com.guaigen.train.generator.util.DbUtil;
import com.guaigen.train.generator.util.Field;
import com.guaigen.train.generator.util.FreemarkerUtil;
import freemarker.template.TemplateException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.*;

public class ServerGenerator {

    static boolean readOnly = false;
    static String vuePath = "web/src/views/main/";
    static String serverPath = "[module]/src/main/java/com/guaigen/train/[module]/";
    static String pomPath = "generator/pom.xml";

    public static void main(String[] args) throws DocumentException, IOException, TemplateException, SQLException {
        // 获取mybatis-generator
        String generatorPath = getGeneratorPath();
        String module = generatorPath.replace("src/main/resources/generator-config-", "").replace(".xml", "");
        System.out.println("module:" + module);
        serverPath = serverPath.replace("[module]", module);
        System.out.println("servicePath:" + serverPath);

        Document document = new SAXReader().read("generator/" + generatorPath);
        Node table = document.selectSingleNode("//table");
        System.out.println(table);
        Node tableName = table.selectSingleNode("@tableName");
        Node domainObjectName = table.selectSingleNode("@domainObjectName");
        System.out.println(tableName.getText() + "/" + domainObjectName.getText());

        // 为DbUtil设置数据源
//        两种用法皆可
//        Node jdbcConnection = document.selectSingleNode("//jdbcConnection");
//        Node connectionURL = jdbcConnection.selectSingleNode("@connectionURL");
//        Node userId = jdbcConnection.selectSingleNode("@userId");
//        Node password = jdbcConnection.selectSingleNode("@password");
        Node connectionURL = document.selectSingleNode("//@connectionURL");
        Node userId = document.selectSingleNode("//@userId");
        Node password = document.selectSingleNode("//@password");
        System.out.println("url:" + connectionURL.getText());
        System.out.println("user:" + userId.getText());
        System.out.println("password:" + password.getText());
        DbUtil.url = connectionURL.getText();
        DbUtil.user = userId.getText();
        DbUtil.password = password.getText();

        // 获取参数
        String Domain = domainObjectName.getText();
        System.out.println("Domain:" + Domain);
        String domain = Domain.substring(0, 1).toLowerCase() + Domain.substring(1);
        System.out.println("domain:" + domain);
        String do_main = tableName.getText().replace("_", "-");
        System.out.println("do_main:" + do_main);
        String tableNameCn = DbUtil.getTableComment(tableName.getText());
        List<Field> fieldList = DbUtil.geColumByTableName(tableName.getText());
        Set<String> typeSet = getJavaTypes(fieldList);

        // 组装参数
        Map<String, Object> param = new HashMap<>();
        param.put("module", module);
        param.put("Domain", Domain);
        param.put("domain", domain);
        param.put("do_main", do_main);
        param.put("tableNameCn", tableNameCn);
        param.put("fieldList", fieldList);
        param.put("typeSet", typeSet);
        param.put("readOnly", readOnly);
        System.out.println("组装参数：" + param);

//        gen(Domain, param, "service", "service");
//        gen(Domain, param, "controller", "controller");
//        gen(Domain, param, "req", "saveReq");
//        gen(Domain, param, "req", "queryReq");
//        gen(Domain, param, "resp", "queryResp");
        genVue(do_main, param);

        // 测试
//        FreemarkerUtil.initConfig("Test.ftl");
//        Map<String, Object> param = new HashMap<>();
//        param.put("domain", "Test");
//        FreemarkerUtil.generator(toPath + "Test.java", param);
    }

    /**
     * 根据字段列表提取Java类型集合
     * 该方法用于遍历字段列表，获取每个字段的Java类型，并将其存入一个集合中
     * 这在分析对象结构或进行类型整理时非常有用
     *
     * @param fields 字段列表，包含了需要提取类型的字段
     * @return 返回一个集合，包含了所有字段的Java类型
     */
    private static Set<String> getJavaTypes(List<Field> fields) {
        // 使用HashSet来存储Java类型，确保类型唯一性
        Set<String> set = new HashSet<>();
        // 遍历字段列表
        for (int i = 0; i < fields.size(); i ++ ) {
            // 获取当前位置的字段
            Field field = fields.get(i);
            // 将字段的Java类型添加到集合中
            set.add(field.getJavaType());
        }
        // 返回包含所有Java类型的集合
        return set;
    }

    /**
     * 根据指定的模板生成代码文件
     * 该方法使用Freemarker模板引擎，根据传入的域名、参数、包名和目标文件名生成相应的Java文件
     * 主要用于自动化代码生成，提高开发效率
     *
     * @param Domain 域名，用于生成文件名
     * @param param 生成代码所需的参数，如类名、属性等
     * @param packageName 目标文件所在的包名，用于确定文件存放目录
     * @param target 目标文件名的基础，实际文件名会根据首字母大写规则进行生成
     * @throws IOException 如果文件操作过程中发生错误
     * @throws TemplateException 如果模板处理过程中发生错误
     */
    private static void gen(String Domain, Map<String, Object> param, String packageName, String target) throws IOException, TemplateException {
        // 初始化Freemarker配置，加载模板文件
        FreemarkerUtil.initConfig(target + ".ftl");
        // 拼接目标文件所在的完整路径
        String toPath = serverPath + packageName + "/";
        // 创建目标文件所在目录，确保目录存在
        new File(toPath).mkdirs();
        // 根据传入的目标文件名，生成首字母大写的新文件名
        String Target = target.substring(0, 1).toUpperCase() + target.substring(1);
        // 拼接目标文件的完整文件名
        String fileName = toPath + Domain + Target + ".java";
        // 打印开始生成的文件名
        System.out.println("开始生成：" + fileName);
        // 调用Freemarker工具类生成文件
        FreemarkerUtil.generator(fileName, param);
    }

    /**
     * 生成Vue组件
     * 使用Freemarker模板引擎根据提供的参数生成Vue组件文件
     *
     * @param do_main 组件的名称，用于生成文件名
     * @param param 包含组件生成所需信息的键值对，如数据、方法等
     * @throws IOException 如果文件操作失败
     * @throws TemplateException 如果模板处理失败
     */
    private static void genVue(String do_main, Map<String, Object> param) throws IOException, TemplateException {
        // 初始化Freemarker配置，指定模板文件
        FreemarkerUtil.initConfig("vue.ftl");
        // 创建Vue组件目录，如果不存在
        new File(vuePath).mkdirs();
        // 拼接Vue组件文件的完整路径和文件名
        String fileName = vuePath + do_main + ".vue";
        // 打印开始生成的文件名
        System.out.println("开始生成：" + fileName);
        // 调用FreemarkerUtil的generator方法生成Vue组件文件
        FreemarkerUtil.generator(fileName, param);
    }

    /**
     * 从POM文件中获取生成器配置文件的路径
     *
     * @return 生成器配置文件的路径
     * @throws DocumentException 如果读取POM文件时发生错误
     */
    private static String getGeneratorPath() throws DocumentException {
        // 创建SAXReader对象用于读取XML文件
        SAXReader saxReader = new SAXReader();
        // 定义一个映射来存储命名空间前缀和URI
        Map<String, String> map = new HashMap<>();
        // 将映射关系添加到映射表中，用于后续的XPath查询
        map.put("pom", "http://maven.apache.org/POM/4.0.0");
        // 设置XPath查询的命名空间URI映射，以便正确解析带有命名空间的XML
        saxReader.getDocumentFactory().setXPathNamespaceURIs(map);
        // 读取POM文件并返回一个Document对象
        Document document = saxReader.read(pomPath);
        // 使用XPath表达式选取POM文件中的configurationFile元素
        Node node = document.selectSingleNode("//pom:configurationFile");
        // 打印configurationFile元素的文本内容，便于调试或日志记录
        System.out.println(node.getText());
        // 返回configurationFile元素的文本内容，即生成器配置文件的路径
        return node.getText();
    }
}
